{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Pandas 复制数据\n\n本教程详细介绍 Pandas 中数据复制的各种方法和注意事项。\n\n## 目录\n1. copy() 方法详解\n2. 深拷贝 vs 浅拷贝\n3. 视图 vs 副本\n4. 复制的常见陷阱\n5. 最佳实践\n6. 性能考虑"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 导入库"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\nimport numpy as np\nimport sys"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. copy() 方法详解\n\n### 方法说明\n\n`copy()` 方法用于创建 DataFrame 或 Series 的副本。\n\n**语法:**\n```python\ndf.copy(deep=True)\n```\n\n**参数:**\n- `deep=True`: 深拷贝（默认），复制数据和索引\n- `deep=False`: 浅拷贝，只复制索引，数据共享\n\n**特点:**\n- ✅ 创建独立的副本\n- ✅ 避免意外修改原数据\n- ✅ 默认是深拷贝\n\n**适用场景:** 需要修改数据但保留原数据时"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例1: 创建示例数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df_original = pd.DataFrame({\n    '姓名': ['张三', '李四', '王五'],\n    '年龄': [25, 30, 35],\n    '城市': ['北京', '上海', '广州']\n})\n\nprint(\"原始 DataFrame:\")\nprint(df_original)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例2: 使用 copy() 创建副本\n\n创建深拷贝，修改副本不影响原数据。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建副本\ndf_copy = df_original.copy()\n\n# 修改副本\ndf_copy.loc[0, '年龄'] = 99\n\nprint(\"修改后的副本:\")\nprint(df_copy)\nprint(\"\\n原始 DataFrame（未改变）:\")\nprint(df_original)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例3: 不使用 copy() 的问题\n\n⚠️ 直接赋值只是创建引用，不是副本。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df_original = pd.DataFrame({\n    '姓名': ['张三', '李四', '王五'],\n    '年龄': [25, 30, 35]\n})\n\n# 错误方式：直接赋值\ndf_reference = df_original\n\n# 修改\"副本\"\ndf_reference.loc[0, '年龄'] = 99\n\nprint(\"修改后的引用:\")\nprint(df_reference)\nprint(\"\\n原始 DataFrame（也被修改了！）:\")\nprint(df_original)\nprint(\"\\n它们是同一个对象:\", df_reference is df_original)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. 深拷贝 vs 浅拷贝\n\n### 方法说明\n\n理解深拷贝和浅拷贝的区别非常重要。\n\n**深拷贝 (deep=True):**\n- 复制所有数据和索引\n- 完全独立的副本\n- 修改副本不影响原数据\n\n**浅拷贝 (deep=False):**\n- 只复制索引结构\n- 数据仍然共享\n- 修改数据会影响原对象\n\n**适用场景:**\n- 深拷贝：需要完全独立的副本（推荐）\n- 浅拷贝：节省内存，只修改索引"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例1: 深拷贝（默认）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.DataFrame({\n    'A': [1, 2, 3],\n    'B': [4, 5, 6]\n})\n\n# 深拷贝\ndf_deep = df.copy(deep=True)\ndf_deep.iloc[0, 0] = 999\n\nprint(\"深拷贝 - 修改后的副本:\")\nprint(df_deep)\nprint(\"\\n原始 DataFrame（未改变）:\")\nprint(df)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例2: 浅拷贝\n\n⚠️ 浅拷贝时，修改数据会影响原对象。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.DataFrame({\n    'A': [1, 2, 3],\n    'B': [4, 5, 6]\n})\n\n# 浅拷贝\ndf_shallow = df.copy(deep=False)\n\n# 修改数据\ndf_shallow.iloc[0, 0] = 999\n\nprint(\"浅拷贝 - 修改后的副本:\")\nprint(df_shallow)\nprint(\"\\n原始 DataFrame（也被修改了！）:\")\nprint(df)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例3: 内存占用对比\n\n深拷贝会占用更多内存。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建较大的 DataFrame\ndf_large = pd.DataFrame(np.random.rand(1000, 100))\n\n# 深拷贝\ndf_deep = df_large.copy(deep=True)\n\n# 浅拷贝\ndf_shallow = df_large.copy(deep=False)\n\nprint(f\"原始 DataFrame 内存: {df_large.memory_usage(deep=True).sum() / 1024:.2f} KB\")\nprint(f\"深拷贝内存: {df_deep.memory_usage(deep=True).sum() / 1024:.2f} KB\")\nprint(f\"浅拷贝内存: {df_shallow.memory_usage(deep=True).sum() / 1024:.2f} KB\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. 视图 vs 副本\n\n### 方法说明\n\nPandas 中的某些操作返回视图（view），某些返回副本（copy）。\n\n**视图 (View):**\n- 不复制数据，只是原数据的另一种查看方式\n- 修改视图会影响原数据\n- 内存效率高\n\n**副本 (Copy):**\n- 复制数据，完全独立\n- 修改副本不影响原数据\n- 占用额外内存\n\n**判断方法:**\n- 使用 `_is_view` 属性（不推荐，内部属性）\n- 使用 `_is_copy` 属性\n- 最安全的方法：总是使用 `copy()`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例1: 切片操作（通常返回视图）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.DataFrame({\n    'A': [1, 2, 3, 4, 5],\n    'B': [10, 20, 30, 40, 50]\n})\n\n# 切片通常返回视图\ndf_slice = df[1:3]\n\nprint(\"切片结果:\")\nprint(df_slice)\n\n# 尝试修改切片（可能会有警告）\ntry:\n    df_slice.loc[1, 'A'] = 999\nexcept Exception as e:\n    print(f\"\\n错误: {e}\")\n\nprint(\"\\n原始 DataFrame:\")\nprint(df)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例2: 链式索引的问题\n\n⚠️ 链式索引可能导致不可预测的行为。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.DataFrame({\n    'A': [1, 2, 3, 4, 5],\n    'B': [10, 20, 30, 40, 50]\n})\n\n# 错误方式：链式索引\n# df[df['A'] > 2]['B'] = 999  # 可能不会生效\n\n# 正确方式：使用 loc\ndf.loc[df['A'] > 2, 'B'] = 999\n\nprint(\"正确修改后:\")\nprint(df)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例3: 明确创建副本避免问题\n\n使用 `copy()` 明确创建副本。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.DataFrame({\n    'A': [1, 2, 3, 4, 5],\n    'B': [10, 20, 30, 40, 50]\n})\n\n# 明确创建副本\ndf_subset = df[df['A'] > 2].copy()\n\n# 修改副本\ndf_subset.loc[:, 'B'] = 999\n\nprint(\"修改后的副本:\")\nprint(df_subset)\nprint(\"\\n原始 DataFrame（未改变）:\")\nprint(df)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. 复制的常见陷阱\n\n常见的复制相关问题和解决方案。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 陷阱1: 直接赋值不是复制\n\n直接赋值只创建引用，不是副本。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df1 = pd.DataFrame({'A': [1, 2, 3]})\n\n# 错误：直接赋值\ndf2 = df1\n\n# 修改 df2\ndf2.loc[0, 'A'] = 999\n\nprint(\"df1 和 df2 是同一个对象:\", df1 is df2)\nprint(\"\\ndf1 (也被修改了):\")\nprint(df1)\n\n# 正确：使用 copy()\ndf3 = df1.copy()\ndf3.loc[0, 'A'] = 111\n\nprint(\"\\ndf3 (独立副本):\")\nprint(df3)\nprint(\"\\ndf1 (未改变):\")\nprint(df1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 陷阱2: 函数参数传递\n\nDataFrame 作为参数传递时是引用传递。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def modify_dataframe(df):\n    \"\"\"修改 DataFrame 的函数\"\"\"\n    df.loc[0, 'A'] = 999\n    return df\n\ndf_original = pd.DataFrame({'A': [1, 2, 3]})\n\n# 传递 DataFrame\ndf_modified = modify_dataframe(df_original)\n\nprint(\"原始 DataFrame (被修改了):\")\nprint(df_original)\n\n# 正确方式：传递副本\ndf_original = pd.DataFrame({'A': [1, 2, 3]})\ndf_modified = modify_dataframe(df_original.copy())\n\nprint(\"\\n传递副本后，原始 DataFrame (未改变):\")\nprint(df_original)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 陷阱3: 列选择的行为\n\n选择单列返回 Series，可能是视图。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.DataFrame({\n    'A': [1, 2, 3],\n    'B': [4, 5, 6]\n})\n\n# 选择单列\ncol_a = df['A']\n\n# 修改列\ncol_a[0] = 999\n\nprint(\"修改列后:\")\nprint(\"col_a:\", col_a.tolist())\nprint(\"\\n原始 DataFrame (可能被修改):\")\nprint(df)\n\n# 安全方式：使用 copy()\ndf = pd.DataFrame({\n    'A': [1, 2, 3],\n    'B': [4, 5, 6]\n})\n\ncol_a_copy = df['A'].copy()\ncol_a_copy[0] = 999\n\nprint(\"\\n使用 copy() 后，原始 DataFrame (未改变):\")\nprint(df)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. 最佳实践\n\n关于数据复制的最佳实践建议。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 实践1: 何时使用 copy()\n\n**需要使用 copy() 的情况:**\n- 需要修改数据但保留原数据\n- 函数参数传递时\n- 从大 DataFrame 中提取子集后要修改\n- 避免 SettingWithCopyWarning\n\n**不需要 copy() 的情况:**\n- 只读操作\n- 临时计算\n- 内存受限且确定不会修改原数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 示例：何时使用 copy()\n\ndf = pd.DataFrame({\n    '姓名': ['张三', '李四', '王五'],\n    '年龄': [25, 30, 35],\n    '工资': [8000, 12000, 15000]\n})\n\n# 场景1: 需要修改数据 - 使用 copy()\ndf_bonus = df.copy()\ndf_bonus['工资'] = df_bonus['工资'] * 1.1\nprint(\"加薪后的数据:\")\nprint(df_bonus)\n\n# 场景2: 只读操作 - 不需要 copy()\navg_age = df['年龄'].mean()\nprint(f\"\\n平均年龄: {avg_age}\")\n\n# 场景3: 提取子集后修改 - 使用 copy()\ndf_high_salary = df[df['工资'] > 10000].copy()\ndf_high_salary['等级'] = '高级'\nprint(\"\\n高薪员工:\")\nprint(df_high_salary)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 实践2: 避免 SettingWithCopyWarning\n\n这是 Pandas 中最常见的警告。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.DataFrame({\n    'A': [1, 2, 3, 4, 5],\n    'B': [10, 20, 30, 40, 50]\n})\n\n# 错误方式（可能产生警告）\n# df[df['A'] > 2]['B'] = 999\n\n# 正确方式1: 使用 loc\ndf.loc[df['A'] > 2, 'B'] = 999\nprint(\"使用 loc 修改:\")\nprint(df)\n\n# 正确方式2: 先 copy 再修改\ndf = pd.DataFrame({\n    'A': [1, 2, 3, 4, 5],\n    'B': [10, 20, 30, 40, 50]\n})\n\ndf_subset = df[df['A'] > 2].copy()\ndf_subset['B'] = 999\nprint(\"\\n使用 copy() 修改:\")\nprint(df_subset)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 实践3: 函数中的数据处理\n\n在函数中处理 DataFrame 的最佳实践。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def process_dataframe_bad(df):\n    \"\"\"不好的方式：直接修改原数据\"\"\"\n    df['新列'] = df['A'] * 2\n    return df\n\ndef process_dataframe_good(df):\n    \"\"\"好的方式：创建副本\"\"\"\n    df_copy = df.copy()\n    df_copy['新列'] = df_copy['A'] * 2\n    return df_copy\n\ndf_original = pd.DataFrame({'A': [1, 2, 3]})\n\n# 使用好的方式\ndf_processed = process_dataframe_good(df_original)\n\nprint(\"处理后的数据:\")\nprint(df_processed)\nprint(\"\\n原始数据（未改变）:\")\nprint(df_original)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 实践4: 明确意图\n\n使用 copy() 明确表达意图，提高代码可读性。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.DataFrame({\n    '姓名': ['张三', '李四', '王五'],\n    '分数': [85, 90, 78]\n})\n\n# 不清晰的代码\ndf_temp = df[df['分数'] > 80]\ndf_temp['等级'] = 'A'\n\n# 清晰的代码\ndf_high_score = df[df['分数'] > 80].copy()  # 明确表示创建副本\ndf_high_score['等级'] = 'A'\n\nprint(\"高分学生:\")\nprint(df_high_score)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. 性能考虑\n\n复制操作的性能影响。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 性能测试: copy() 的开销"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import time\n\n# 创建大型 DataFrame\ndf_large = pd.DataFrame(np.random.rand(10000, 100))\n\n# 测试深拷贝\nstart = time.time()\ndf_deep = df_large.copy(deep=True)\ntime_deep = time.time() - start\n\n# 测试浅拷贝\nstart = time.time()\ndf_shallow = df_large.copy(deep=False)\ntime_shallow = time.time() - start\n\n# 测试直接赋值\nstart = time.time()\ndf_ref = df_large\ntime_ref = time.time() - start\n\nprint(\"性能对比（10000行 x 100列）:\")\nprint(f\"深拷贝:   {time_deep:.6f} 秒\")\nprint(f\"浅拷贝:   {time_shallow:.6f} 秒\")\nprint(f\"直接赋值: {time_ref:.6f} 秒\")\nprint(f\"\\n深拷贝比直接赋值慢 {time_deep/time_ref if time_ref > 0 else 0:.0f} 倍\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 内存使用对比"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建 DataFrame\ndf = pd.DataFrame(np.random.rand(1000, 50))\n\n# 计算内存使用\nmem_original = df.memory_usage(deep=True).sum()\nmem_deep = df.copy(deep=True).memory_usage(deep=True).sum()\nmem_shallow = df.copy(deep=False).memory_usage(deep=True).sum()\n\nprint(\"内存使用对比:\")\nprint(f\"原始 DataFrame: {mem_original / 1024:.2f} KB\")\nprint(f\"深拷贝:         {mem_deep / 1024:.2f} KB\")\nprint(f\"浅拷贝:         {mem_shallow / 1024:.2f} KB\")\nprint(f\"\\n深拷贝额外占用: {(mem_deep - mem_original) / 1024:.2f} KB\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 优化建议"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 建议1: 只在必要时使用 copy()\ndf = pd.DataFrame({'A': range(1000)})\n\n# 不需要 copy - 只读操作\nresult = df['A'].sum()\n\n# 需要 copy - 要修改数据\ndf_modified = df.copy()\ndf_modified['B'] = df_modified['A'] * 2\n\nprint(\"优化建议1: 只在必要时复制\")\nprint(f\"计算结果: {result}\")\n\n# 建议2: 使用 inplace 参数（某些操作）\ndf = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})\n\n# 不推荐：创建副本\n# df_new = df.drop('B', axis=1)\n\n# 推荐：如果不需要保留原数据\ndf.drop('B', axis=1, inplace=True)\nprint(\"\\n优化建议2: 适当使用 inplace\")\nprint(df)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 总结\n\n### 复制方法对比\n\n| 操作 | 结果 | 数据独立 | 内存占用 | 适用场景 |\n|------|------|---------|---------|----------|\n| 直接赋值 `df2 = df1` | 引用 | ❌ | 无额外 | 只读操作 |\n| 浅拷贝 `copy(deep=False)` | 共享数据 | 部分 | 少量 | 只修改索引 |\n| 深拷贝 `copy(deep=True)` | 完全独立 | ✅ | 翻倍 | 需要修改数据 |\n\n### 关键要点\n\n1. **默认使用深拷贝** `copy()` 或 `copy(deep=True)`\n2. **直接赋值不是复制**，只是创建引用\n3. **函数参数传递** 时考虑是否需要 copy()\n4. **避免链式索引**，使用 `loc` 进行修改\n5. **明确使用 copy()** 可以避免 SettingWithCopyWarning\n6. **性能考虑**：copy() 有开销，只在必要时使用\n7. **浅拷贝** 节省内存但要小心数据共享\n8. **提取子集后修改** 时一定要 copy()\n\n### 最佳实践\n\n```python\n# ✅ 推荐\ndf_copy = df.copy()  # 明确创建副本\ndf.loc[条件, 列] = 值  # 使用 loc 修改\n\n# ❌ 避免\ndf2 = df  # 只是引用\ndf[条件][列] = 值  # 链式索引\n```"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.8.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}